﻿namespace WebPacker
{
	using System;
	using System.Collections.Generic;
	using System.Web;
	using WebPacker.Compressors;
	using WebPacker.Logger;
	using WebPacker.Packs;
	using WebPacker.Providers;
	using WebPacker.Repositories;

	public sealed class WebPackerRouter : IPackRouter, IDisposable
	{
		private static readonly object syncRoot = new object();
		private static readonly ILogger logger = LogFactory.GetLogger();

		private readonly IPackProvider packProvider;
		private readonly IPackRepository packRepository;
		private readonly IList<ICompressor> compressors;
		private readonly ICompressor nullCompressor;

		private static IPackRouter instance;

		private bool disposed;

		public WebPackerRouter(IPackRepository packRepository, IPackProvider packProvider)
		{
			if (packProvider == null)
			{
				throw new ArgumentNullException("packProvider");
			}

			if (packRepository == null)
			{
				throw new ArgumentNullException("packRepository");
			}

			this.packProvider = packProvider;
			this.packRepository = packRepository;

			this.nullCompressor = new NullCompressor();

			this.compressors = new List<ICompressor>();
			this.compressors.Add(new GZipCompressor());
		}

		public static IPackRouter Instance
		{
			get
			{
				return instance;
			}

			internal set
			{
				if (value == null)
				{
					throw new ArgumentNullException("value");
				}

				lock (syncRoot)
				{
					instance = value;
				}
			}
		}

		public string Url
		{
			get { return packProvider.Url ?? "~/webpacker.axd"; }
		}

		public bool Enabled { get; set; }

		public bool ThrowsExceptions { get; set; }

		public bool CompressionEnabled { get; set; }

		public IPack GetPack(PackType packType, string packName)
		{
			try
			{
				return packRepository.GetPack(packType, packName);
			}
			catch (Exception e)
			{
				logger.Exception(string.Format("Error at get pack {0}", LogHelper.ToString(packType, packName)), e);

				if (ThrowsExceptions)
				{
					throw;
				}
			}

			return null;
		}

		public void Process(HttpContextBase context)
		{
			packProvider.Process(context);
		}

		public string GetUrl(HttpContextBase context, PackType packType, string packName)
		{
			try
			{
				var pack = packRepository.GetPack(packType, packName);
				return GetUrl(context, pack);
			}
			catch (Exception e)
			{
				logger.Exception(string.Format("Error at route pack {0}.", LogHelper.ToString(packType, packName)), e);

				if (ThrowsExceptions)
				{
					throw;
				}
			}

			return null;
		}

		public string GetUrl(HttpContextBase context, PackType packType, string[] resources)
		{
			try
			{
				var pack = packRepository.GetPack(packType, resources);
				return GetUrl(context, pack);
			}
			catch (Exception e)
			{
				logger.Exception(string.Format("Error at route resources {0}.", LogHelper.ToString(packType, resources)), e);

				if (ThrowsExceptions)
				{
					throw;
				}
			}

			return null;
		}

		public void Dispose()
		{
			if (!disposed)
			{
				if (packRepository is IDisposable)
				{
					((IDisposable)packRepository).Dispose();
				}

				if (packProvider is IDisposable)
				{
					((IDisposable)packProvider).Dispose();
				}

				disposed = true;
			}
		}

		private string GetUrl(HttpContextBase context, IPack pack)
		{
			return packProvider.GetUrl(GetCompressor(context), pack);
		}

		private ICompressor GetCompressor(HttpContextBase context)
		{
			if (CompressionEnabled && context != null)
			{
				var userAgent = context.Request.UserAgent;
				var acceptEncoding = context.Request.Headers["Accept-Encoding"];

				if (!userAgent.Contains("MSIE 6") || userAgent.Contains("SV1"))
				{
					foreach (var item in compressors)
					{
						if (item.Accept(acceptEncoding))
						{
							return item;
						}
					}
				}
			}

			return nullCompressor;
		}
	}
}